from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponseRedirect, JsonResponse
from django.urls import reverse, reverse_lazy
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views import generic
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.db.models import Count, Avg
from .models import Book, Author, BookInstance, Genre, Language, BookRating, BookReview, BorrowHistory
import datetime

# Create your views here.

from .models import Book, Author, BookInstance, Genre, Language

def index(request):
    """View function for home page of site."""
    # Generate counts of some of the main objects
    num_books = Book.objects.all().count()
    num_instances = BookInstance.objects.all().count()
    # Available copies of books
    num_instances_available = BookInstance.objects.filter(
        status__exact='a').count()
    num_authors = Author.objects.count()  # The 'all()' is implied by default.

    # Number of visits to this view, as counted in the session variable.
    num_visits = request.session.get('num_visits', 0)
    num_visits += 1
    request.session['num_visits'] = num_visits

    # Render the HTML template index.html with the data in the context variable.
    return render(
        request,
        'index.html',
        context={'num_books': num_books, 'num_instances': num_instances,
                 'num_instances_available': num_instances_available, 'num_authors': num_authors,
                 'num_visits': num_visits},
    )

from django.views import generic


class BookListView(generic.ListView):
    """Generic class-based view for a list of books."""
    model = Book
    paginate_by = 10

class BookDetailView(generic.DetailView):
    """Generic class-based detail view for a book."""
    model = Book

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['user_rating'] = None
        context['user_review'] = None
        if self.request.user.is_authenticated:
            context['user_rating'] = BookRating.objects.filter(
                book=self.object, user=self.request.user).first()
            context['user_review'] = BookReview.objects.filter(
                book=self.object, user=self.request.user).first()
        context['reviews'] = BookReview.objects.filter(book=self.object).select_related('user')
        context['is_favorite'] = self.object.favorites.filter(id=self.request.user.id).exists() if self.request.user.is_authenticated else False
        context['is_liked'] = self.object.likes.filter(id=self.request.user.id).exists() if self.request.user.is_authenticated else False
        return context

class AuthorListView(generic.ListView):
    """Generic class-based list view for a list of authors."""
    model = Author
    paginate_by = 10

class AuthorDetailView(generic.DetailView):
    """Generic class-based detail view for an author."""
    model = Author


class GenreDetailView(generic.DetailView):
    """Generic class-based detail view for a genre."""
    model = Genre

class GenreListView(generic.ListView):
    """Generic class-based list view for a list of genres."""
    model = Genre
    paginate_by = 10

class LanguageDetailView(generic.DetailView):
    """Generic class-based detail view for a genre."""
    model = Language

class LanguageListView(generic.ListView):
    """Generic class-based list view for a list of genres."""
    model = Language
    paginate_by = 10

class BookInstanceListView(generic.ListView):
    """Generic class-based view for a list of books."""
    model = BookInstance
    paginate_by = 10

class BookInstanceDetailView(generic.DetailView):
    """Generic class-based detail view for a book."""
    model = BookInstance

from django.contrib.auth.mixins import LoginRequiredMixin

class LoanedBooksByUserListView(LoginRequiredMixin, generic.ListView):
    """Generic class-based view listing books on loan to current user."""
    model = BookInstance
    template_name = 'catalog/bookinstance_list_borrowed_user.html'
    paginate_by = 10

    def get_queryset(self):
        return (
            BookInstance.objects.filter(borrower=self.request.user)
            .filter(status__exact='o')
            .order_by('due_back')
        )

# Added as part of challenge!
from django.contrib.auth.mixins import PermissionRequiredMixin


class LoanedBooksAllListView(PermissionRequiredMixin, generic.ListView):
    """Generic class-based view listing all books on loan. Only visible to users with can_mark_returned permission."""
    model = BookInstance
    permission_required = 'catalog.can_mark_returned'
    template_name = 'catalog/bookinstance_list_borrowed_all.html'
    paginate_by = 10

    def get_queryset(self):
        return BookInstance.objects.filter(status__exact='o').order_by('due_back')

from django.shortcuts import get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse
import datetime
from django.contrib.auth.decorators import login_required, permission_required
from catalog.forms import RenewBookForm


@login_required
@permission_required('catalog.can_mark_returned', raise_exception=True)
def renew_book_librarian(request, pk):
    """View function for renewing a specific BookInstance by librarian."""
    book_instance = get_object_or_404(BookInstance, pk=pk)

    # If this is a POST request then process the Form data
    if request.method == 'POST':

        # Create a form instance and populate it with data from the request (binding):
        form = RenewBookForm(request.POST)

        # Check if the form is valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
            book_instance.due_back = form.cleaned_data['renewal_date']
            book_instance.save()

            # redirect to a new URL:
            return HttpResponseRedirect(reverse('all-borrowed'))

    # If this is a GET (or any other method) create the default form
    else:
        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date})

    context = {
        'form': form,
        'book_instance': book_instance,
    }

    return render(request, 'catalog/book_renew_librarian.html', context)


from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Author


class AuthorCreate(PermissionRequiredMixin, CreateView):
    model = Author
    fields = ['first_name', 'last_name', 'date_of_birth', 'date_of_death']
    initial = {'date_of_death': '11/11/2023'}
    permission_required = 'catalog.add_author'

class AuthorUpdate(PermissionRequiredMixin, UpdateView):
    model = Author
    # Not recommended (potential security issue if more fields added)
    fields = '__all__'
    permission_required = 'catalog.change_author'

class AuthorDelete(PermissionRequiredMixin, DeleteView):
    model = Author
    success_url = reverse_lazy('authors')
    permission_required = 'catalog.delete_author'

    def form_valid(self, form):
        try:
            self.object.delete()
            return HttpResponseRedirect(self.success_url)
        except Exception as e:
            return HttpResponseRedirect(
                reverse("author-delete", kwargs={"pk": self.object.pk})
            )

# Classes created for the forms challenge


class BookCreate(PermissionRequiredMixin, CreateView):
    model = Book
    fields = ['title', 'author', 'summary', 'isbn', 'genre', 'language']
    permission_required = 'catalog.add_book'


class BookUpdate(PermissionRequiredMixin, UpdateView):
    model = Book
    fields = ['title', 'author', 'summary', 'isbn', 'genre', 'language']
    permission_required = 'catalog.change_book'


class BookDelete(PermissionRequiredMixin, DeleteView):
    model = Book
    success_url = reverse_lazy('books')
    permission_required = 'catalog.delete_book'

    def form_valid(self, form):
        try:
            self.object.delete()
            return HttpResponseRedirect(self.success_url)
        except Exception as e:
            return HttpResponseRedirect(
                reverse("book-delete", kwargs={"pk": self.object.pk})
            )


class GenreCreate(PermissionRequiredMixin, CreateView):
    model = Genre
    fields = ['name', ]
    permission_required = 'catalog.add_genre'


class GenreUpdate(PermissionRequiredMixin, UpdateView):
    model = Genre
    fields = ['name', ]
    permission_required = 'catalog.change_genre'


class GenreDelete(PermissionRequiredMixin, DeleteView):
    model = Genre
    success_url = reverse_lazy('genres')
    permission_required = 'catalog.delete_genre'


class LanguageCreate(PermissionRequiredMixin, CreateView):
    model = Language
    fields = ['name', ]
    permission_required = 'catalog.add_language'


class LanguageUpdate(PermissionRequiredMixin, UpdateView):
    model = Language
    fields = ['name', ]
    permission_required = 'catalog.change_language'


class LanguageDelete(PermissionRequiredMixin, DeleteView):
    model = Language
    success_url = reverse_lazy('languages')
    permission_required = 'catalog.delete_language'


class BookInstanceCreate(PermissionRequiredMixin, CreateView):
    model = BookInstance
    fields = ['book', 'imprint', 'due_back', 'borrower', 'status']
    permission_required = 'catalog.add_bookinstance'


class BookInstanceUpdate(PermissionRequiredMixin, UpdateView):
    model = BookInstance
    # fields = "__all__"
    fields = ['imprint', 'due_back', 'borrower', 'status']
    permission_required = 'catalog.change_bookinstance'


class BookInstanceDelete(PermissionRequiredMixin, DeleteView):
    model = BookInstance
    success_url = reverse_lazy('bookinstances')
    permission_required = 'catalog.delete_bookinstance'

@login_required
def rate_book(request, pk):
    """View function for rating a book."""
    if request.method == 'POST':
        book = get_object_or_404(Book, pk=pk)
        rating_value = int(request.POST.get('rating'))
        if 1 <= rating_value <= 5:
            rating, created = BookRating.objects.update_or_create(
                book=book,
                user=request.user,
                defaults={'rating': rating_value}
            )
            return JsonResponse({'success': True, 'average_rating': book.average_rating})
    return JsonResponse({'success': False}, status=400)

@login_required
def review_book(request, pk):
    """View function for reviewing a book."""
    book = get_object_or_404(Book, pk=pk)
    if request.method == 'POST':
        review_text = request.POST.get('review_text')
        if review_text:
            review, created = BookReview.objects.update_or_create(
                book=book,
                user=request.user,
                defaults={'review_text': review_text}
            )
            return redirect('book-detail', pk=pk)
    return redirect('book-detail', pk=pk)

@login_required
def toggle_favorite(request, pk):
    """View function for toggling favorite status of a book."""
    book = get_object_or_404(Book, pk=pk)
    if book.favorites.filter(id=request.user.id).exists():
        book.favorites.remove(request.user)
        is_favorite = False
    else:
        book.favorites.add(request.user)
        is_favorite = True
    return JsonResponse({'success': True, 'is_favorite': is_favorite})

@login_required
def toggle_like(request, pk):
    """View function for toggling like status of a book."""
    book = get_object_or_404(Book, pk=pk)
    if book.likes.filter(id=request.user.id).exists():
        book.likes.remove(request.user)
        is_liked = False
    else:
        book.likes.add(request.user)
        is_liked = True
    return JsonResponse({'success': True, 'is_liked': is_liked})

class PopularBooksListView(generic.ListView):
    """View for displaying books sorted by popularity (number of borrows)."""
    model = Book
    template_name = 'catalog/book_list_popular.html'
    paginate_by = 10

    def get_queryset(self):
        return Book.objects.annotate(
            borrow_count=Count('bookinstance__borrowhistory')
        ).order_by('-borrow_count')

class UserFavoriteBooksListView(LoginRequiredMixin, generic.ListView):
    """View for displaying user's favorite books."""
    model = Book
    template_name = 'catalog/book_list_favorites.html'
    paginate_by = 10

    def get_queryset(self):
        return Book.objects.filter(favorites=self.request.user)

@login_required
def like_review(request, pk):
    """View function for liking a review."""
    review = get_object_or_404(BookReview, pk=pk)
    if review.likes.filter(id=request.user.id).exists():
        review.likes.remove(request.user)
        is_liked = False
    else:
        review.likes.add(request.user)
        is_liked = True
    return JsonResponse({'success': True, 'is_liked': is_liked})
